package com.leyou.common.aop;

import com.leyou.common.annotation.ServiceLog;
import com.leyou.common.util.IpUtil;
import com.leyou.common.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zqq
 * @ClassName ServiceLogAspect
 * @Description: 切面类，为有ServiceLog注解的接口，打印日志
 * @date 2020/3/24-14:33
 */
@Slf4j
@Aspect
@Component
public class ServiceLogAspect {
    ThreadLocal<StopWatch> stopWatch = new ThreadLocal<>();

    @Pointcut("@annotation(serviceLog)")
    public void serviceLogHander(ServiceLog serviceLog) {
    }

    @Before("serviceLogHander(serviceLog)")
    public void doBefore(JoinPoint joinPoint, ServiceLog serviceLog) {
        StopWatch sw = new StopWatch();
        sw.start();
        stopWatch.set(sw);

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取切入点所在的方法
        Method method = signature.getMethod();

        String operateType = ""; // 定义操作方式
        String operateContent = ""; // 定义操作内容

        // 获取注解中的操作方式
        if (method != null && !"".equals(method)) {
            // 获取用户操作方式
            operateType = serviceLog.operateType();
            // 获取用户操作内容
            operateContent = serviceLog.operateContent();
        }

        String className = joinPoint.getTarget().getClass().getName();
        String methodName = className + "." + method.getName();
        String methodObj = request.getMethod();
        String URL = request.getRequestURI().toString();
        String IP = IpUtil.getIp(request);
        String userId = request.getHeader("X-User");


        // 获取请求的参数
        String classname = joinPoint.getTarget().getClass().getName();
        String methodname = classname + "." + method.getName();
        String argsname[] = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
        Map<String, Object> parammap = new HashMap<>();
        if (argsname.length > 0) {
            parammap = getParam(joinPoint, argsname, methodname);
        }

        String detail = JsonUtils.serialize(parammap);

        StringBuffer requestLog = new StringBuffer();
        requestLog.append("请求信息: ")
                .append("操作方式  = {" + operateType + "},\t")
                .append("操作内容  = {" + operateContent + "},\t")
                .append("请求方式  = {" + methodObj + "},\t")
                .append("请求userId = {" + userId + "},\t")
                .append("请求地址URL = {" + IP + URL + "},\t")
                .append("请求IP地址  = {" + IP + "},\t")
                .append("请求方法名  = {" + methodName + "},\t")
                .append("请求参数名称   = {" + request.getParameterNames().toString() + "},\t")
                .append("请求参数Query   = {" + request.getQueryString() + "},\t")
                .append("请求参数Body = " + detail + ",\t");
        log.info("==================================================");
        log.info(requestLog.toString());
    }

    @AfterThrowing(value = "serviceLogHander(serviceLog)", throwing = "ex")
    public void doAfterThrowing(JoinPoint joinPoint, ServiceLog serviceLog, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        log.error("连接点方法为: " + methodName + ", 参数为: " + args + " 异常为: ", ex);
    }

    @AfterReturning(value = "serviceLogHander(serviceLog)", returning = "result")
    public void resultInfo(JoinPoint joinPoint, ServiceLog serviceLog, Object result) throws Exception {
        String detail = JsonUtils.serialize(result);
        log.info("返回参数  = " + detail);
        stopWatch.get().stop();
        log.info("处理耗时：{} ms（毫秒）", stopWatch.get().getTime());
        log.info("==================================================");
    }

    /**
     * 处理参数格式,并返回需要的参数
     *
     * @Title: getParam
     * @param joinPoint
     * @param argsname
     * @param methodname
     * @return Map<String,Object>
     * @author 张永胜
     */
    public static Map<String, Object> getParam(JoinPoint joinPoint, String argsname[], String methodname) {
        Map<String, Object> detailmap = new HashMap<>();
        Map<String, Object> map = new HashMap<>();
        // 获取参数值
        Object args[] = joinPoint.getArgs();
        // 获取参数名
        argsname = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
        for (int i = 0; i < argsname.length; i++) {
            if (!argsname[i].equals("model")) {
                if(args[i] instanceof HttpServletRequest)
                    continue;
                map.put(argsname[i], args[i]);
            }
        }
        detailmap.put("method", methodname);
        detailmap.put("params", map);
        return detailmap;
    }
}
